#include <common/localmachine/localmachine.h>

#include <QHostInfo>
#include <QList>
#include <QNetworkInterface>
#include <QString>

#ifdef _WIN32
#include <Wbemidl.h>
#include <comdef.h>
#include <windows.h>
#pragma comment(lib, "wbemuuid.lib")
#else
#include <fstream>
#include <sstream>
#include <unistd.h>
#endif

// 获取主机名
std::string LocalMachine::getHostName()
{
	return QHostInfo::localHostName().toStdString();
}

// 获取第一个 IPv4 地址
std::string LocalMachine::getIpAddress()
{
	for (const QHostAddress& address: QNetworkInterface::allAddresses())
	{
		if (address.protocol() == QAbstractSocket::IPv4Protocol && !address.isLoopback())
		{
			return address.toString().toStdString();
		}
	}
	return {};
}

// 获取所有 IPv4 地址，逗号分隔
std::string LocalMachine::getAllIpAddress()
{
	QStringList ipList;
	for (const QHostAddress& address: QNetworkInterface::allAddresses())
	{
		if (address.protocol() == QAbstractSocket::IPv4Protocol && !address.isLoopback())
		{
			ipList << address.toString();
		}
	}
	return ipList.join(",").toStdString();
}

// 获取第一个 MAC 地址
std::string LocalMachine::getMac(bool needFormat)
{
	for (const QNetworkInterface& iface: QNetworkInterface::allInterfaces())
	{
		if (!(iface.flags() & QNetworkInterface::IsLoopBack) &&
			iface.flags() & QNetworkInterface::IsUp)
		{
			std::string mac = iface.hardwareAddress().toStdString();
			if (!needFormat)
			{
				mac = normalizeMacAddress(mac);
			}
			return mac;
		}
	}
	return {};
}

// 获取所有 MAC 地址，逗号分隔
std::string LocalMachine::getMacAddressesAsString(bool needFormat)
{
	QStringList macList;
	for (const QNetworkInterface& iface: QNetworkInterface::allInterfaces())
	{
		if (!(iface.flags() & QNetworkInterface::IsLoopBack) &&
			iface.flags() & QNetworkInterface::IsUp)
		{
			macList << iface.hardwareAddress();
		}
	}
	if (needFormat)
	{
		return macList.join(",").toStdString();
	}
	else
	{
		std::vector<std::string> vec;

		vec.reserve(macList.size());
		for (const QString& str: macList)
		{
			vec.push_back(normalizeMacAddress(str.toStdString()));
		}

		// 使用 std::accumulate 拼接
		std::string result = std::accumulate(
				std::next(vec.begin()), vec.end(), vec[0],
				[](const std::string& a, const std::string& b) {
					return a + "," + b;
				});

		return result;
	}
}

// 格式化 MAC 地址（例如 "001A2B3C4D5E" -> "00:1A:2B:3C:4D:5E"）
std::string LocalMachine::formatMacAddress(const std::string& macAddress)
{
	std::string result;
	for (size_t i = 0; i < macAddress.size(); ++i)
	{
		result.push_back(toupper(macAddress[i]));
		if (i % 2 == 1 && i != macAddress.size() - 1)
		{
			result.push_back(':');
		}
	}
	return result;
}
std::string LocalMachine::normalizeMacAddress(const std::string& macAddress)
{
	std::string res;
	res.reserve(macAddress.size());
	for (char c: macAddress)
	{
		if (c != ':' && c != '-')// 去掉常见分隔符
		{
			res.push_back(std::toupper(static_cast<unsigned char>(c)));
		}
	}
	return res;
}

#ifdef _WIN32
/**
 * @brief 使用 WMI 获取 CPU ID
 *
 * 说明：
 * - 通过 WMI 查询 `Win32_Processor.ProcessorId` 获取 CPU 序列号（ID）。
 * - WMI（Windows Management Instrumentation）提供了访问硬件信息的统一接口。
 *
 * 注意：
 * - 不是所有硬件厂商都会提供有效的 CPU 序列号，有些机器可能返回空。
 * - 需要初始化 COM 库（CoInitializeEx），并设置安全性（CoInitializeSecurity）。
 * - 最终释放所有 COM 对象，避免内存泄漏。
 *
 * @return std::string CPU ID，如果获取失败返回空字符串
 */
std::string LocalMachine::getCPUId()
{
	HRESULT hres;
	// 初始化 COM 库，使用多线程并发模式
	hres = CoInitializeEx(0, COINIT_MULTITHREADED);
	if (FAILED(hres)) return {};

	// 初始化 COM 安全设置
	hres = CoInitializeSecurity(NULL, -1, NULL, NULL,
								RPC_C_AUTHN_LEVEL_DEFAULT,
								RPC_C_IMP_LEVEL_IMPERSONATE,
								NULL, EOAC_NONE, NULL);
	if (FAILED(hres)) return {};

	// 创建 WMI Locator，用于连接 WMI 服务
	IWbemLocator* pLoc = NULL;
	hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
							IID_IWbemLocator, (LPVOID*)&pLoc);
	if (FAILED(hres)) return {};

	// 连接到 WMI 服务 (ROOT\CIMV2 命名空间)
	IWbemServices* pSvc = NULL;
	hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
	if (FAILED(hres)) return {};

	// 执行 WQL 查询，获取 CPU ProcessorId
	IEnumWbemClassObject* pEnumerator = NULL;
	hres = pSvc->ExecQuery(bstr_t("WQL"),
						   bstr_t("SELECT ProcessorId FROM Win32_Processor"),
						   WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
						   NULL, &pEnumerator);
	if (FAILED(hres)) return {};

	// 遍历查询结果，取第一个 ProcessorId
	IWbemClassObject* pclsObj = NULL;
	ULONG uReturn = 0;
	std::string cpuId;
	while (pEnumerator)
	{
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
		if (0 == uReturn) break;
		VARIANT vtProp;
		hr = pclsObj->Get(L"ProcessorId", 0, &vtProp, 0, 0);
		if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
		{
			cpuId = _bstr_t(vtProp.bstrVal);// 转换为 std::string
		}
		VariantClear(&vtProp);
		pclsObj->Release();
	}

	// 释放资源
	pSvc->Release();
	pLoc->Release();
	CoUninitialize();
	return cpuId;
}

/**
 * @brief 使用 WMI 获取主板序列号
 *
 * 说明：
 * - 通过 WMI 查询 `Win32_BaseBoard.SerialNumber` 获取主板序列号。
 *
 * 注意：
 * - 一些厂商 BIOS 没有写入主板序列号时，会返回
 *   "To be filled by O.E.M.", "Default string", "Standard" 等无效值。
 * - 如果主板序列号不可用，可以考虑备用查询：
 *   - Win32_BIOS.SerialNumber （BIOS 序列号）
 *   - Win32_BaseBoard.Product （主板型号）
 *
 * @return std::string 主板序列号，如果获取失败返回空字符串
 */
std::string LocalMachine::getMainBoardId()
{
	HRESULT hres;
	// 初始化 COM 库
	hres = CoInitializeEx(0, COINIT_MULTITHREADED);
	if (FAILED(hres)) return {};

	// 初始化 COM 安全设置
	hres = CoInitializeSecurity(NULL, -1, NULL, NULL,
								RPC_C_AUTHN_LEVEL_DEFAULT,
								RPC_C_IMP_LEVEL_IMPERSONATE,
								NULL, EOAC_NONE, NULL);
	if (FAILED(hres)) return {};

	// 创建 WMI Locator
	IWbemLocator* pLoc = NULL;
	hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
							IID_IWbemLocator, (LPVOID*)&pLoc);
	if (FAILED(hres)) return {};

	// 连接 WMI 服务
	IWbemServices* pSvc = NULL;
	hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
	if (FAILED(hres)) return {};

	// 查询主板序列号
	IEnumWbemClassObject* pEnumerator = NULL;
	hres = pSvc->ExecQuery(bstr_t("WQL"),
						   bstr_t("SELECT SerialNumber FROM Win32_BaseBoard"),
						   WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
						   NULL, &pEnumerator);
	if (FAILED(hres)) return {};

	// 遍历结果
	IWbemClassObject* pclsObj = NULL;
	ULONG uReturn = 0;
	std::string boardId;
	while (pEnumerator)
	{
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
		if (0 == uReturn) break;
		VARIANT vtProp;
		hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
		if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
		{
			boardId = _bstr_t(vtProp.bstrVal);// 转换为 std::string
		}
		VariantClear(&vtProp);
		pclsObj->Release();
	}

	// 释放资源
	pSvc->Release();
	pLoc->Release();
	CoUninitialize();
	return boardId;
}

#else// Linux

// 从 /proc/cpuinfo 读取 CPU 序列号
std::string LocalMachine::getCPUId()
{
	std::ifstream cpuinfo("/proc/cpuinfo");
	std::string line;
	while (std::getline(cpuinfo, line))
	{
		if (line.find("Serial") != std::string::npos)
		{
			auto pos = line.find(":");
			if (pos != std::string::npos)
			{
				return line.substr(pos + 2);
			}
		}
	}
	return {};
}

// 从 /sys/devices/virtual/dmi/id/board_serial 读取主板序列号
std::string LocalMachine::getMainBoardId()
{
	std::ifstream file("/sys/devices/virtual/dmi/id/board_serial");
	if (!file.is_open()) return {};
	std::string id;
	std::getline(file, id);
	return id;
}
#endif
